home *** CD-ROM | disk | FTP | other *** search
- /******************************************************
- *
- * fp2ratio.c
- *
- * Floating-point number to ratio approximation.
- *
- * Usage: fp2ratio number
- *
- * Compiler: Microsoft C 6.0 using inline
- * floating-point emulator (option /FPi).
- *
- *****************************************************/
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <math.h>
-
- /* Maximum argument significant digits. */
- #define MAX_SIG_DIGITS 9
-
- #define ARRAY_SIZE 2
- #define I ( i ) % ARRAY_SIZE
- #define I_MINUS_1 ( i - 1 ) % ARRAY_SIZE
- #define I_MINUS_2 ( i ) % ARRAY_SIZE
-
- typedef unsigned long Ulong;
- typedef long double Ldouble;
-
-
- int main( int argc, char **argv )
- {
- int i, dec_digits, sign;
- Ulong n0, n, d0, d, temp;
- Ulong p[ARRAY_SIZE] = { 0, 1 };
- Ulong q[ARRAY_SIZE] = { 1, 0 };
- double x;
- Ldouble percent_err;
-
- /* Check for one command-line argument. */
- if ( argc != 2 )
- {
- fprintf(stderr, "Usage: %s number\n", argv[0]);
- exit( EXIT_FAILURE );
- }
- else /* argc == 2 */
- x = strtod( argv[1], (char **) NULL );
-
- /* Handle zero and negative arguments. */
- if ( x < 0.0 )
- {
- sign = -1;
- x = -x;
- }
- else if ( x == 0.0 )
- {
- puts( "\n0:\n" );
- puts( " 0 / 1 Exactly!" );
- exit( EXIT_SUCCESS );
- }
- else /* x > 0.0 */
- sign = 1;
-
- /* Check for out-of-range arguments. */
- if ( x >= pow( 10.0, (double) MAX_SIG_DIGITS ) )
- {
- fprintf( stderr, "%s: Magnitude is", argv[1] );
- fprintf( stderr, " too large.\n" );
- exit( EXIT_FAILURE );
- }
- else if ( x <=
- pow( 10.0, (double) -MAX_SIG_DIGITS) / 2.0 )
- {
- fprintf( stderr, "%s: Magnitude is", argv[1] );
- fprintf( stderr, " too small.\n" );
- exit( EXIT_FAILURE );
- }
-
- /* Determine the argument's radix-10 ratio. */
- d0 = (Ulong) pow( 10.0, (double) MAX_SIG_DIGITS -
- ((x < 1.0) ? 0.0 : floor( 1.0 + log10( x ))));
- n0 = (Ulong) ( ( x * (double) d0 ) + 0.5 );
- printf( "\n%.*g:\n\n", MAX_SIG_DIGITS,
- (double) n0 / (double) d0 );
-
- /* Iteratively determine integer ratios. */
- for ( i = 2, d = d0, n = n0 ; ;
- i++, temp = d, d = n % d, n = temp )
- {
- p[I] = n / d * p[I_MINUS_1] + p[I_MINUS_2];
- q[I] = n / d * q[I_MINUS_1] + q[I_MINUS_2];
-
- /* Print ratios with non-zero numerators. */
- if ( p[I] != 0 )
- {
- printf("%11ld / %-10lu", sign * p[I], q[I]);
- if ( n % d == 0 )
- {
- printf( " Exactly!\n" );
- break;
- }
-
- /*
- * Compute the ratio's percent error
- * (taking care to avoid significance
- * loss when subtracting nearly equal
- * values):
- *
- * percent error =
- *
- *
- * 100 * ( p[i] * d0 - q[i] * n0 )
- * -------------------------------------
- * q[I] * n0
- *
- * Display in %f format with at least two
- * significant digits.
- */
- percent_err = (Ldouble) p[I] * (Ldouble) d0;
- percent_err -= (Ldouble) q[I] * (Ldouble) n0;
- percent_err *= 100.0L;
- percent_err /= (Ldouble) q[I] * (Ldouble) n0;
-
- dec_digits =
- ( fabs( (double) percent_err ) >= 10.0 ) ?
- 1 : 1 + ( int ) ( fabs( floor(
- log10( fabs( (double) percent_err )))));
- printf( "%+*.*Lf%%\n", dec_digits + 6,
- dec_digits, percent_err );
- }
- }
-
- exit( EXIT_SUCCESS );
- }
-
-